//  This file is part of NHibernate.ReLinq.Sample a sample showing
//  the use of the open source re-linq library to implement a non-trivial 
//  Linq-provider, on the example of NHibernate (www.nhibernate.org).
//  Copyright (C) 2005-2009 rubicon informationstechnologie gmbh, www.rubicon.eu
//  
//  NHibernate.ReLinq.Sample is based on re-motion re-linq (http://www.re-motion.org/).
//  
//  NHibernate.ReLinq.Sample is free software; you can redistribute it 
//  and/or modify it under the terms of the MIT License 
// (http://www.opensource.org/licenses/mit-license.php).
// 
using System;
using System.Linq.Expressions;
using System.Linq;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.ResultOperators;

namespace NHibernate.ReLinq.Sample.HqlQueryGeneration
{
    public class HqlGeneratorQueryModelVisitor : QueryModelVisitorBase
    {
        public static CommandData GenerateHqlQuery(QueryModel queryModel)
        {
            var visitor = new HqlGeneratorQueryModelVisitor();
            // 自定义的 QueryModelVisitor
            // 对数据进行解析
            // 解析的数据存储在 _queryParts 和 _parameterAggregator 两个变量中
            visitor.VisitQueryModel(queryModel);

            // 通过两个临时变量构建 CommmanData
            // 此对象回可以返回 NHibernate.IQuery
            // 从而可以查询数据库
            return visitor.GetHqlCommand();
        }

        // Instead of generating an HQL string, we could also use a NHibernate ASTFactory to generate IASTNodes.

        // 保存解析过程中的主语句
        // FromParts WhereParts OrderByParts 和 SelectPart
        private readonly QueryPartsAggregator _queryParts = new QueryPartsAggregator();

        // List<NamedParameter>，用于保存参数名和值
        private readonly ParameterAggregator _parameterAggregator = new ParameterAggregator();

        public CommandData GetHqlCommand()
        {
            return new CommandData(_queryParts.BuildHQLString(), _parameterAggregator.GetParameters());
        }

        /// <summary>
        /// 重写默认的 VisitQueryModel
        /// 默认的 VisitQueryModel 也会访问 MainFromClause、VisitBodyClauses、SelectClause、VisitResultOperators
        /// 可以在重写的位置调整相应顺序
        /// </summary>
        /// <param name="queryModel"></param>
        public override void VisitQueryModel(QueryModel queryModel)
        {
            // 执行 VisitMainSelectClause
            queryModel.SelectClause.Accept(this, queryModel);
            // 执行 VisitMainFromClause
            queryModel.MainFromClause.Accept(this, queryModel);

            // 解析 Where 语句
            VisitBodyClauses(queryModel.BodyClauses, queryModel);

            // 解析 Count() 类型操作语句
            VisitResultOperators(queryModel.ResultOperators, queryModel);
        }

        /// <summary>
        /// 调用类似 Count() 方法时会执行
        /// </summary>
        /// <param name="resultOperator"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index)
        {
            // 对于 Count() 方法的调用
            if (resultOperator is CountResultOperator)
            {
                var tp = string.Format("cast(count({0}) as int)", _queryParts.SelectPart);
                _queryParts.SelectPart = tp;

                Console.WriteLine($"SelectPart -> {tp}");
            }
            else
                throw new NotSupportedException("Only Count() result operator is showcased in this sample. Adding Sum, Min, Max is left to the reader.");

            base.VisitResultOperator(resultOperator, queryModel, index);
        }

        /// <summary>
        /// 调用 from 语句时执行
        /// </summary>
        /// <param name="fromClause"></param>
        /// <param name="queryModel"></param>
        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
        {
            // 构建 FromParts
            // 类似 {NHibernate.ReLinq.Sample.UnitTests.DomainObjects.PhoneNumber} as {pn}
            _queryParts.AddFromPart(fromClause);
            base.VisitMainFromClause(fromClause, queryModel);
        }

        /// <summary>
        /// 调用 select 语句时执行
        /// </summary>
        /// <param name="selectClause"></param>
        /// <param name="queryModel"></param>
        public override void VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
        {
            // 构建 SelectPart
            // 传递的是一个 select pn 表达式数
            // 最后解析出来的是 pn
            _queryParts.SelectPart = GetHqlExpression(selectClause.Selector);
            base.VisitSelectClause(selectClause, queryModel);
        }

        /// <summary>
        /// 调用 where 语句时执行
        /// </summary>
        /// <param name="whereClause"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
        {
            // 构建 WhereParts
            _queryParts.AddWherePart(GetHqlExpression(whereClause.Predicate));
            base.VisitWhereClause(whereClause, queryModel, index);
        }

        /// <summary>
        /// 调用 order by 语句时执行
        /// </summary>
        /// <param name="orderByClause"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitOrderByClause(OrderByClause orderByClause, QueryModel queryModel, int index)
        {
            // 构建 OrderByParts
            _queryParts.AddOrderByPart(orderByClause.Orderings.Select(o => GetHqlExpression(o.Expression)));
            base.VisitOrderByClause(orderByClause, queryModel, index);
        }

        /// <summary>
        /// 调用 join 语句时执行
        /// </summary>
        /// <param name="joinClause"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index)
        {
            // Join 语句需要额外处理
            // 添加 FormParts 和 WhereParts

            // HQL joins work differently, need to simulate using a cross join with a where condition

            _queryParts.AddFromPart(joinClause);
            _queryParts.AddWherePart(
                "({0} = {1})",
                GetHqlExpression(joinClause.OuterKeySelector),
                GetHqlExpression(joinClause.InnerKeySelector));

            base.VisitJoinClause(joinClause, queryModel, index);
        }

        /// <summary>
        /// 调用除了主 from 语句之外的额外 from 语句时执行
        /// 例如多表 join 时会有额外的 from 语句
        /// </summary>
        /// <param name="fromClause"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitAdditionalFromClause(AdditionalFromClause fromClause, QueryModel queryModel, int index)
        {
            // 添加 FromParts
            _queryParts.AddFromPart(fromClause);
            base.VisitAdditionalFromClause(fromClause, queryModel, index);
        }

        /// <summary>
        /// 调用 group join 语句时执行
        /// </summary>
        /// <param name="groupJoinClause"></param>
        /// <param name="queryModel"></param>
        /// <param name="index"></param>
        public override void VisitGroupJoinClause(GroupJoinClause groupJoinClause, QueryModel queryModel, int index)
        {
            // 不支持 Grup Join 这种复杂语法
            throw new NotSupportedException("Adding a join ... into ... implementation to the query provider is left to the reader for extra points.");
        }

        // 每一个 QueryModel 的执行
        // 都通过自定义的 HqlGeneratorExpressionTreeVisitor 来转换 Hql
        private string GetHqlExpression(Expression expression)
        {
            // Hql 语句解析器
            // _parameterAggregator 在解析的过程中进行填充

            var tp = HqlGeneratorExpressionTreeVisitor.GetHqlExpression(expression, _parameterAggregator);

            Console.WriteLine($"$GetHqlExpression -> {tp}");


            return tp;
        }
    }
}